/**
 * \file  utils.h
 * \brief Definition of Antiviral Set implementation class.
 *
 * LAV: Lib Anti Virals
 * <http://lav.googlecode.com/>
 * Copyright (C) 2013  Boretto Martin, Lenarduzzi Federico, 
 *                     Alberione Fernando, Ocampo Emanuel FuDePAN
 *
 * This file is part of the LAV project.
 *
 * Contents:       Header file for LAV providing utils.
 *
 * System:         LAV
 * Language:       C++
 *
 *
 * Author:         Boretto Martin
 * E-Mail:         martinboretto@gmail.com
 *
 * Author:         Lenarduzzi Federico
 * E-Mail:         federicolenarduzzi@gmail.com
 * 
 * Author:         Alberione Fernando
 * E-Mail:         falberione037@gmail.com
 *
 * Author:         Ocampo Emanuel
 * E-Mail:         eocampo86@gmail.com
 *
 * LAV is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * LAV is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with LAV.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include <iostream>
#include <list>
#include <set>
#include <combeng/combeng.h>


/**
 * Performs the cartesian product. The "in" parameter contains all sets (list) which have to be processed.
 */
template <class T>
void cartesian_product(const std::list< std::list<T> >& in, std::list< std::list<T> >& res)
{
    //The observer for the combination policy used bellow
    class IsolatedObserver: public comb_eng::CombinationObserver<T>
    {
    public:

        IsolatedObserver(std::list< std::list<T> >* const result) :
            combinations(result)
        {}

        //When a combination is generated, It is added to a container.
        virtual void update(const std::list<T>& combination)
        {
            combinations->push_back(combination);
        }

    private:
        std::list< std::list<T> >* combinations;
    };

    //Use a paralell combination policy to perform the cartesian product.
    IsolatedObserver* obs = new IsolatedObserver(&res);
    ComposedCombinationPolicyParallel<T>* composed_pol = new ComposedCombinationPolicyParallel <T>(obs, "composed");

    typename std::list< std::list<T> >::const_iterator it;
    std::list <T> all_elemetns;

    //for each list "Y" in the entry parameter, a list_combination_policy is added using that list "Y" as its reference set.
    for (it = in.begin(); it != in.end(); ++it)
    {
        ListCombinationPolicy<T>* list_comPol = new ListCombinationPolicy<T>(NULL, "list");
        composed_pol->add_new_combination_policy(list_comPol, *it, true);
        all_elemetns.insert(all_elemetns.begin(), it->begin(), it->end());
    }

    comb_eng::Status status;
    composed_pol->combine(all_elemetns, status);

    delete composed_pol; // It also removes all list policies which it contains.
    delete obs;
}


/**
 * Uses the newtonian combination policy to get all posible combinations with a fixed
 * size using a list of elements as entry,
 */
template <class T>
void expand_newtonian(const std::list<T>& in, uint32_t min, uint32_t max, std::list< std::list<T> >& out)
{
    // The observer for the combination policy used bellow
    class IsolatedObserver: public comb_eng::CombinationObserver<T>
    {
    public:

        IsolatedObserver(std::list< std::list<T> >* const result) :
            combinations(result)
        {}

        virtual void update(const std::list<T>& combination)
        {
            combinations->push_back(combination);
        }

    private:
        std::list< std::list<T> >* combinations;
    };

    // Creates the combination policy
    IsolatedObserver* obs = new IsolatedObserver(&out);
    NewtonianCombinationPolicy<T>* newtonian = new NewtonianCombinationPolicy<T>(obs, min, max, "ExpNewtonian");

    // Build the combinations
    comb_eng::Status status;
    newtonian->combine(in, status);

    delete(obs);
    delete(newtonian);
}